package mbsl.books;

import java.util.ArrayList;
import java.util.List;

import mbsl.commons.Commons;
import mbsl.commons.base.BasicElementController;
import mbsl.commons.base.ElementEditController;
import mbsl.commons.lib.BasicElement;
import mbsl.commons.lib.Location;
import mbsl.storage.Database;
import mbsl.system.TextProvider;
import mbsl.ui.dialogs.Dialogs;
import mbsl.ui.interfaces.Listener;
import mbsl.util.MessageLog;

import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.widgets.Shell;

public class BookEditController extends ElementEditController {
	public BookEditController(BookController pParent, Shell pParentWindow) {
		super(pParent.getCore().getDatabase());
		
		mDialog = new BookEditDialog(pParentWindow);
		mParent = pParent;
		mDatabase = mParent.getCore().getDatabase();
		mErrorLog = new MessageLog();
		
		mDialog.setTitleShadowText(tp.get("title.shadow"));

		// All the world's controllers
		mFormatController = new BasicElementController<Format>(mDatabase, Format.class);
		mGenreController = new BasicElementController<BookGenre>(mDatabase, BookGenre.class);
		mAuthorController = new BasicElementController<Author>(mDatabase, Author.class) {
			@Override
			public String getSearchQuery(String pData) {
				Author author = new Author();
				
				author.setName(pData);
				
				return "where lower(surname) = lower('" + author.getSurname() + "') and lower(firstName) = lower('" + author.getFirstName() + "')";
			}
			// Because of Author's special setName() (which we just exploited), we don't need to override fillData()
		};
		mPredecessorController = new BasicElementController<Book>(mDatabase, Book.class);
		mAdvancedSearchController = new AdvancedSearchController<Book>(
				pParent.getCore(),
				Book.class,
				mDialog.getPredecessor(), // <-- The widget to which it's attached
				mDialog.getStatusBar(),
				tp, 
				"(lower(name) like '%%%1$s%%' or lower(author.firstName) like '%%%1$s%%' or lower(author.surname) like '%%%1$s%%')")
		;
		
		mAuthorController.setListArguments("order by lower(surname), lower(firstName)");
		mPredecessorController.setListArguments("order by lower(author.surname), lower(author.firstname), name");
		
		// Action listeners
		//  Dialogue save button
		mDialog.addSaveListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent pE) {
				if (save()) {
					for (Listener listener : mListeners)
						listener.alert();
					
					mDialog.close();
				}
			}
		});
		//  Dialogue closing
		mDialog.addCloseListener(new Listener() {
			@Override
			public void alert() {
				if (mElement.getName() != null)
					mDialog.getPredecessor().add(mElement);
			}
		});
		// Add data to the combo boxes, or the combo boxes to the data (providers).
		mDialog.getPredecessor().add(getElementList(Book.class));

		mAuthorController.addCombo(mDialog.getAuthorField());
		mFormatController.addCombo(mDialog.getFormat());
		mGenreController.addCombo(mDialog.getGenre());
		getLocationController().addCombo(mDialog.getLocation());
	}
	/** The same as calling setElement(null) */
	public void newElement() {
		setElement(null);
	}
	/** Notifies the EditController that an element is removed, so that it can update its lists. */
	public void notifyRemoved(Book pElement) {
		mDialog.getPredecessor().remove(pElement);
	}
	/**
	 * Puts the data from the passed Game into the dialogue.
	 * <p>
	 * If {@code null} is passed, a new game will be created, and the fields will be blank.
	 */
	public void setElement(Book pElement) {
		if (pElement == null)
			mElement = new Book();
		else
			mElement = pElement;

		mAdvancedSearchController.setElement(mElement);
		
		if (mElement.getName() == null || mElement.getName().isEmpty())
			mDialog.setWindowTitle(tp.get("title.new"));
		else
			mDialog.setWindowTitle(tp.get("title"));
		
		// Set up the fields
		mDialog.setName(mElement.getName());
		
		mDialog.getAuthorField().setSelected(mElement.getAuthor());
		mDialog.getFormat().setSelected(mElement.getFormat());
		
		if (mElement.getLocation() != null)
			mDialog.getLocation().setSelected(mElement.getLocation());
		else // If there's no recorded previous location, there will be no selection (null)
			mDialog.getLocation().setSelected(mPreviousLocation);
		
		// In several places here, we're taking advantage of the UI elements (or their wrappers) being able to take null as not set.
		mDialog.getPredecessor().setSelected(mElement.getParent());
		mDialog.getGenre().setSelected(mElement.getGenre());
		mDialog.getRating().setValue(mElement.getRating() > 0 ? mElement.getRating() : 0); // Compatibility. Previously negative values meant not set.
		mDialog.getYear().setValue(mElement.getYear() != null ? Integer.parseInt(mElement.getYear()) : null);

		if (mElement.getName() != null)
			mDialog.getPredecessor().remove(mElement);
		
		mDialog.getStatusBar().setInfo(Commons.tp.get("status.ready"));
	}
	public Book getElement() {
		return mElement;
	}
	
	/** Attempts to save the data. Returns false if something went wrong. */
	private boolean save() {
		if (mDialog.getName().trim().isEmpty())
			mErrorLog.addMessage(tp.get("error.noname"));

		// Author
		Author author = mAuthorController.getBasicElement(mDialog.getAuthorField().getSelected());

		if (author == null)
			mErrorLog.addMessage(tp.get("error.noauthor"));
		else
			mElement.setAuthor(author);
		// Name
		mElement.setName(mDialog.getName());
		// Rating
		mElement.setRating(mDialog.getRating().getValue());
		// Predecessor
		if (!(mDialog.getPredecessor().getSelected() instanceof String)) // This will be null, a String, or a Book.  The first and the last we can deal with, so eliminate the one in the middle
			mElement.setParent(mPredecessorController.getBasicElement(mDialog.getPredecessor().getSelected()), true);
		// Genre
		mElement.setGenre(mGenreController.getBasicElement(mDialog.getGenre().getSelected()));
		// Year
		mElement.setYear(mDialog.getYear().getValue() != null ? mDialog.getYear().getValue().toString() : null);
		// Location
		mElement.setLocation(getLocationController().getBasicElement(mDialog.getLocation().getSelected()));
		// Format
		mElement.setFormat(mFormatController.getBasicElement(mDialog.getFormat().getSelected()));
		
		if (!mErrorLog.isEmpty()) {
			StringBuilder message = new StringBuilder();
			
			for (int i = 0; i < mErrorLog.getList().size(); i++) {
				message.append(mErrorLog.getList().get(i));
				
				if (i < mErrorLog.getList().size() - 1)
					message.append('\n');
			}
			
			Dialogs.error(mDialog.getShell(), message.toString(), tp.get("error.header"));

			mErrorLog.clear();
			
			return false;
		}
		
		mPreviousLocation = mElement.getLocation();
		
		mDatabase.save(mElement);

		mDialog.getPredecessor().add(mElement);
		
		return true;
	}
	/**
	 * Convenience method for fetching a database listing of class <i>pClass</i> in order <i>pOrderString</i>
	 * @param pClass The class of the objects to list.  Must extend BasicElement.
	 * @param pOrderString The order in which to sort.  Takes HQL, and 'order by' is already included.
	 */
	protected List<?> getElementList(Class<? extends BasicElement> pClass, String pArguments) {
		return mParent.getCore().getDatabase().getList(pClass, pArguments);
	}
	/**
	 * Convenience method for fetching a database listing of class <i>pClass</i> ordered by name
	 * @param pClass The class of the objects to list.  Must extend BasicElement.
	 */
	protected List<?> getElementList(Class<? extends BasicElement> pClass) {
		return getElementList(pClass, "order by lower(name)");
	}
	public void addChangeListener(Listener pListener) {
		if (mListeners == null)
			mListeners = new ArrayList<Listener>();
		
		mListeners.add(pListener);
	}
	public void open() {
		mDialog.open();
	}

	private BasicElementController<Book> mPredecessorController;
	private BasicElementController<Format> mFormatController;
	private BasicElementController<BookGenre> mGenreController;
	private BasicElementController<Author> mAuthorController;
	
	private MessageLog mErrorLog;
	private AdvancedSearchController<Book> mAdvancedSearchController;
	private Database mDatabase;
	private List<Listener> mListeners;
	private TextProvider tp = Books.tp.prefix("books.edit");
	private Location mPreviousLocation;
	private Book mElement;
	private BookController mParent;
	private BookEditDialog mDialog;
}
